/* Copyright (C) 2001, 2009 United States Government as represented by
the Administrator of the National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.examples;

import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.ElevationModel;
import gov.nasa.worldwind.layers.Layer;
import gov.nasa.worldwind.terrain.CompoundElevationModel;
import gov.nasa.worldwind.util.Logging;

import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;

/**
 * Displays UI components for a set of caller specified imported data, and manages creation of World Wind components
 * from that data. Callers fill the panel with imported data by invoking {@link #addImportedData(gov.nasa.worldwind.DataConfiguration,
 * gov.nasa.worldwind.avlist.AVList)}. This adds the UI components for a specified data set (a "Go To" button, and a
 * label description), creates a World Wind component from the DataConfiguration, and adds the component to the World
 * Window passed to the panel during construction.
 *
 * @author dcollins
 * @version $Id: ImportedDataPanel.java 13188 2010-03-06 00:46:44Z dcollins $
 */
public class ImportedDataPanel extends JPanel
{
    protected WorldWindow worldWindow;
    protected JPanel dataConfigPanel;

    /**
     * Constructs an ImportedDataPanel with the specified title and WorldWindow. Upon construction, the panel is
     * configured to accept imported data via calls to {@link #addImportedData(gov.nasa.worldwind.DataConfiguration,
     * gov.nasa.worldwind.avlist.AVList)}.
     *
     * @param title       the panel's title, displayed in a titled border.
     * @param worldWindow the panel's WorldWindow, which any World Wind components are added to.
     *
     * @throws IllegalArgumentException if the WorldWindow is null.
     */
    public ImportedDataPanel(String title, WorldWindow worldWindow)
    {
        if (worldWindow == null)
        {
            String message = Logging.getMessage("nullValue.WorldWindow");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.worldWindow = worldWindow;
        this.dataConfigPanel = new JPanel(new GridLayout(0, 1, 0, 4));
        this.layoutComponents(title);
    }

    /**
     * Adds the UI components for the specified imported data to this panel, and adds the World Wind component created
     * from the data to the WorldWindow passed to this panel during construction.
     *
     * @param dataConfig the DataConfiguration describing a World Wind components configuration.
     * @param params     a parameter list overriding or extending information contained in the DataConfiguration.
     *
     * @throws IllegalArgumentException if the DataConfiguration is null.
     */
    public void addImportedData(final DataConfiguration dataConfig, final AVList params)
    {
        if (dataConfig == null)
        {
            String message = Logging.getMessage("nullValue.DataConfigurationIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.addToWorldWindow(dataConfig, params);

        String description = this.getDescription(dataConfig);
        Sector sector = this.getSector(dataConfig);

        Box box = Box.createHorizontalBox();
        box.add(new JButton(new GoToSectorAction(sector)));
        box.add(Box.createHorizontalStrut(10));
        box.add(new JLabel(description));

        this.dataConfigPanel.add(box);
        this.revalidate();
    }

    protected void layoutComponents(String title)
    {
        this.dataConfigPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); // top, left, bottom, right
        // Put the grid in a container to prevent scroll panel from stretching its vertical spacing.
        JPanel dummyPanel = new JPanel(new BorderLayout());
        dummyPanel.add(this.dataConfigPanel, BorderLayout.NORTH);
        // Add the dummy panel to a scroll pane.
        JScrollPane scrollPane = new JScrollPane(dummyPanel);
        scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); // top, left, bottom, right
        // Add the scroll pane to a titled panel that will resize with the main window.
        JPanel titlePanel = new JPanel(new GridLayout(0, 1, 0, 10)); // rows, cols, hgap, vgap
        titlePanel.setBorder(
            new CompoundBorder(BorderFactory.createEmptyBorder(9, 9, 9, 9), new TitledBorder(title)));
        titlePanel.add(scrollPane);

        this.setLayout(new GridLayout(0, 1, 0, 0)); // rows, cols, hgap, vgap
        this.add(titlePanel);
    }

    //**************************************************************//
    //********************  DataConfiguration Utils  ***************//
    //**************************************************************//

    protected String getDescription(DataConfiguration dataConfig)
    {
        StringBuilder sb = new StringBuilder(dataConfig.getName());

        if (dataConfig.getType().equalsIgnoreCase("Layer"))
        {
            sb.append(" (Layer)");
        }
        else if (dataConfig.getType().equalsIgnoreCase("ElevationModel"))
        {
            sb.append(" (Elevations)");
        }

        return sb.toString();
    }

    protected Sector getSector(DataConfiguration dataConfig)
    {
        return dataConfig.getSector("Sector");
    }

    protected void addToWorldWindow(DataConfiguration dataConfig, AVList params)
    {
        if (dataConfig.getType().equalsIgnoreCase("Layer"))
        {
            this.addLayerToWorldWindow(dataConfig, params);
        }
        else if (dataConfig.getType().equalsIgnoreCase("ElevationModel"))
        {
            this.addElevationModelToWorldWindow(dataConfig, params);
        }
    }

    protected void addLayerToWorldWindow(DataConfiguration dataConfig, AVList params)
    {
        Layer layer = null;
        try
        {
            Factory factory = (Factory) WorldWind.createConfigurationComponent(AVKey.LAYER_FACTORY);
            layer = (Layer) factory.createFromDataConfig(dataConfig, params);
        }
        catch (Exception e)
        {
            String message = Logging.getMessage("generic.CreationFromDataConfigurationFailed", dataConfig.getName());
            Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
        }

        if (layer == null)
            return;

        layer.setEnabled(true); // TODO: BasicLayerFactory creates layer which is intially disabled

        if (!this.worldWindow.getModel().getLayers().contains(layer))
            ApplicationTemplate.insertBeforePlacenames(this.worldWindow, layer);
    }

    protected void addElevationModelToWorldWindow(DataConfiguration dataConfig, AVList params)
    {
        // Provide default extreme elevations for the ElevationModel using the minimum and maximum elevations on
        // Earth.
        AVList props = (params != null) ? params.copy() : new AVListImpl();
        props.setValue(AVKey.ELEVATION_MIN, -11000d); // Depth of Mariana trench.
        props.setValue(AVKey.ELEVATION_MAX, 8500d);   // Height of Mt. Everest.

        ElevationModel em = null;
        try
        {
            Factory factory = (Factory) WorldWind.createConfigurationComponent(AVKey.ELEVATION_MODEL_FACTORY);
            em = (ElevationModel) factory.createFromDataConfig(dataConfig, props);
        }
        catch (Exception e)
        {
            String message = Logging.getMessage("generic.CreationFromDataConfigurationFailed", dataConfig.getName());
            Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
        }

        if (em == null)
            return;

        CompoundElevationModel cm;

        if (this.worldWindow.getModel().getGlobe().getElevationModel() instanceof CompoundElevationModel)
        {
            cm = (CompoundElevationModel) this.worldWindow.getModel().getGlobe().getElevationModel();
        }
        else
        {
            cm = new CompoundElevationModel();
            cm.addElevationModel(this.worldWindow.getModel().getGlobe().getElevationModel());
            this.worldWindow.getModel().getGlobe().setElevationModel(cm);
        }

        if (!cm.containsElevationModel(em))
            cm.addElevationModel(em);
    }

    //**************************************************************//
    //********************  Actions  *******************************//
    //**************************************************************//

    protected class GoToSectorAction extends AbstractAction
    {
        protected Sector sector;

        public GoToSectorAction(Sector sector)
        {
            super("Go To");
            this.sector = sector;
            this.setEnabled(this.sector != null);
        }

        public void actionPerformed(ActionEvent e)
        {
            Extent extent = worldWindow.getModel().getGlobe().computeBoundingCylinder(
                worldWindow.getSceneController().getVerticalExaggeration(), this.sector);

            Angle fov = worldWindow.getView().getFieldOfView();
            Position centerPos = new Position(this.sector.getCentroid(), 0d);
            double zoom = extent.getRadius() / fov.cosHalfAngle() / fov.tanHalfAngle();

            worldWindow.getView().goTo(centerPos, zoom);
        }
    }
}
